package com.beemans.topon.ads

import android.view.ViewGroup
import androidx.annotation.Keep
import androidx.core.view.updateLayoutParams
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import com.anythink.core.api.ATAdConst
import com.anythink.core.api.ATAdInfo
import com.anythink.core.api.AdError
import com.anythink.nativead.api.*
import com.anythink.network.gdt.GDTATConst
import com.anythink.network.toutiao.TTATConst
import com.beemans.topon.kts.context
import com.beemans.topon.kts.deepClone
import com.beemans.topon.kts.lifecycleOwnerEx
import com.beemans.topon.log.Logger
import com.beemans.topon.views.NativeAdLayout
import com.qq.e.ads.nativ.ADSize
import kotlinx.coroutines.Job
import java.util.concurrent.ConcurrentHashMap

/**
 * @author tiamosu
 * @date 2022/10/31
 */
object TpNativeAd {
    private val logTag by lazy { this::class.java.simpleName }
    private val adLoaderMap by lazy { ConcurrentHashMap<String, MutableList<TpNativeAdLoader>>() }

    /**
     * 开始加载广告
     *
     * @param owner 传入 Activity 或 Fragment
     * @param config 广告配置
     * @param flContainer 广告视图容器
     * @param nativeSelfRender 自渲染配置
     * @param nativeAdCallback 广告回调
     */
    fun start(
        owner: LifecycleOwner,
        config: TpNativeAdConfig,
        flContainer: ATNativeAdView? = null,
        nativeSelfRender: INativeSelfRender? = null,
        nativeAdCallback: (TpNativeAdCallback.() -> Unit)? = null
    ) {
        val loader =
            TpNativeAdLoader(owner, config, flContainer, nativeSelfRender, nativeAdCallback)
        start(loader)
    }

    /**
     * 开始加载广告
     *
     * @param loader 广告加载器
     */
    fun start(loader: TpNativeAdLoader) {
        val placementId = loader.config.placementId
        val loaders = adLoaderMap.getOrPut(placementId) { mutableListOf() }
        if (!loaders.contains(loader)) {
            loaders.add(loader)
            adLoaderMap[placementId] = loaders
        }
        //当前有其他相同广告位正在加载广告时，等待加载
        if (loaders.any { it.isAdRequesting() }) {
            return
        }
        loader.start()
    }

    /**
     * 开始加载列表中最近的一个
     */
    private fun startNext(oldLoader: TpNativeAdLoader) {
        val placementId = oldLoader.config.placementId
        val loaders = adLoaderMap[placementId]
        loaders?.lastOrNull()?.let { start(it) }
    }

    /**
     * 从列表中移除对应的加载
     */
    private fun removeLoader(
        loader: TpNativeAdLoader,
        callback: (list: List<TpNativeAdLoader>) -> Unit = {}
    ) {
        val placementId = loader.config.placementId
        val loaders = adLoaderMap[placementId]
        if (loaders.isNullOrEmpty()) return
        loaders.remove(loader)
        adLoaderMap[placementId] = loaders
        callback.invoke(loaders)
    }

    /**
     * 广告加载器
     *
     * @param owner 传入 Activity 或 Fragment
     * @param config 广告配置
     * @param flContainer 广告视图容器
     * @param nativeSelfRender 自渲染配置
     * @param nativeAdCallback 广告回调
     */
    class TpNativeAdLoader(
        private val owner: LifecycleOwner,
        val config: TpNativeAdConfig,
        private val flContainer: ATNativeAdView? = null,
        private val nativeSelfRender: INativeSelfRender? = null,
        private val nativeAdCallback: (TpNativeAdCallback.() -> Unit)? = null
    ) : DefaultLifecycleObserver, ATNativeEventListener, ATNativeNetworkListener,
        ATNativeDislikeListener() {
        private val context by lazy { owner.context }
        private val placementId by lazy { config.placementId }
        private val adViewWidth by lazy { config.adViewWidth }
        private val adViewHeight by lazy { config.adViewHeight }

        private var tpAdStatus: TpNativeAdStatus? = null
        private var callback = nativeAdCallback?.let { TpNativeAdCallback().apply(it) }
        private var nativeAdLayout: ATNativeAdView? = flContainer
        private var atNative: ATNative? = null
        private var nativeAd: NativeAd? = null
        private var renderJob: Job? = null
        private var prepareExInfo: ATNativePrepareExInfo? = null

        init {
            init()
        }

        private fun init() {
            owner.lifecycleOwnerEx.lifecycle.addObserver(this)

            flContainer?.updateLayoutParams {
                this.width = adViewWidth
                this.height = ViewGroup.LayoutParams.WRAP_CONTENT
            }
        }

        override fun onNativeAdLoaded() {
            onAdLoaded()
        }

        override fun onNativeAdLoadFail(adError: AdError?) {
            onAdLoadFailed(adError)
        }

        override fun onAdImpressed(adView: ATNativeAdView?, atAdInfo: ATAdInfo?) {
            Logger.logI(logTag, "onAdShow --- atAdInfo:$atAdInfo")
            callback?.onAdShow?.invoke(atAdInfo)

            if (config.isAutoLoadAd && atNative?.checkAdStatus()?.isReady != true) {
                Logger.logI(logTag, "onAdShow --- autoLoadAd")
                val newConfig = config.deepClone()?.apply { isOnlyPreload = true }
                newConfig?.let { start(owner, it) }
            }
        }

        override fun onAdClicked(adView: ATNativeAdView?, atAdInfo: ATAdInfo?) {
            Logger.logI(logTag, "onAdClick --- atAdInfo:$atAdInfo")
            callback?.onAdClick?.invoke(atAdInfo)
        }

        override fun onAdVideoStart(adView: ATNativeAdView?) {
        }

        override fun onAdVideoEnd(adView: ATNativeAdView?) {
        }

        override fun onAdVideoProgress(adView: ATNativeAdView?, progress: Int) {
        }

        override fun onAdCloseButtonClick(adView: ATNativeAdView?, atAdInfo: ATAdInfo?) {
            val isCloseAd = callback?.onAdClose?.invoke(atAdInfo) ?: true
            if (isCloseAd) {
                Logger.logI(logTag, "onAdClose --- atAdInfo:$atAdInfo")
                onAdRelease()
            }
        }

        /**
         * 是否正在请求广告
         */
        fun isAdRequesting(): Boolean {
            return tpAdStatus?.isRequesting == true
        }

        /**
         * 获取广告加载器
         */
        private fun getAtNative(): ATNative {
            return ATNative(context, placementId, this).apply {
                setLocalExtra(mutableMapOf<String, Any>().apply {
                    //配置广告宽高
                    put(ATAdConst.KEY.AD_WIDTH, adViewWidth)
                    put(ATAdConst.KEY.AD_HEIGHT, adViewHeight)

                    //广告自适应高度
                    if (config.isAdaptiveHeight) {
                        put(TTATConst.NATIVE_AD_IMAGE_HEIGHT, 0)    //穿山甲
                        put(GDTATConst.AD_HEIGHT, ADSize.AUTO_HEIGHT)   //广点通
                    }
                })
            }
        }

        /**
         * 开始加载广告
         */
        internal fun start() {
            if (atNative == null) {
                atNative = getAtNative()
            }
            if (nativeAdLayout == null) {
                nativeAdLayout = flContainer
            }
            if (callback == null) {
                callback = nativeAdCallback?.let { TpNativeAdCallback().apply(it) }
            }
            tpAdStatus = TpNativeAdStatus(placementId).apply {
                isStarted = true
                isRequesting = true
            }

            Logger.logI(logTag, "onAdStart")
            callback?.onAdStart?.invoke(this)

            if (atNative?.checkAdStatus()?.isReady == true) {
                onAdLoaded()
                return
            }
            atNative?.makeAdRequest()
        }

        /**
         * 展示广告
         */
        private fun showAd() {
            tpAdStatus?.isShowing = true

            var isShowAd = false
            renderJob?.cancel()
            owner.lifecycleScope.launchWhenResumed {
                isShowAd = true
                var isLocalVisible = true
                if (config.isVisibleRender && nativeAdLayout is NativeAdLayout) {
                    isLocalVisible = (nativeAdLayout as NativeAdLayout).isLocalVisible
                }
                if (!isLocalVisible) {
                    tpAdStatus?.isVisibleRender = true
                    return@launchWhenResumed
                }
                startRender()
            }.also {
                renderJob = it
            }.invokeOnCompletion {
                if (isShowAd || renderJob?.isCancelled == true) return@invokeOnCompletion
                onAdShowFailed(3)
            }
        }

        /**
         * 开始渲染广告
         */
        private fun startRender() {
            val nativeAd = atNative?.nativeAd
            if (nativeAd == null) {
                onAdShowFailed(1)
                return
            }
            this.nativeAd?.destory()
            this.nativeAd = nativeAd
            nativeAd.setNativeEventListener(this)
            nativeAd.setDislikeCallbackListener(this)
            prepareExInfo = ATNativePrepareExInfo()

            renderAd()
        }

        /**
         * 重新渲染广告
         */
        fun reRenderAd() {
            renderAd()
        }

        /**
         * 渲染广告
         */
        private fun renderAd() {
            val nativeAd = this.nativeAd ?: return
            nativeAdLayout?.removeAllViews()
            kotlin.runCatching {
                if (nativeAd.isNativeExpress) {
                    if (config.isSelfRender) {
                        onAdShowFailed(4)
                        return
                    }
                    nativeAd.renderAdContainer(nativeAdLayout, null)
                    nativeAd.prepare(nativeAdLayout, prepareExInfo)
                    onAdRender()
                    return
                }
                val selfRenderView = nativeSelfRender?.getSelfRenderView()
                if (selfRenderView == null || context == null) {
                    onAdShowFailed(2)
                    return
                }
                prepareExInfo?.let {
                    nativeSelfRender?.onBindView(context!!, nativeAd.adMaterial, it)
                }
                nativeAd.renderAdContainer(nativeAdLayout, selfRenderView)
                nativeAd.prepare(nativeAdLayout, prepareExInfo)
                onAdRender()
            }.onFailure {
                Logger.logI(logTag, "showAd --- onFailure:${it.message}")
            }
        }

        /**
         * 广告渲染
         */
        private fun onAdRender() {
            Logger.logI(logTag, "onAdRender")
            callback?.onAdRender?.invoke()
        }

        /**
         * 广告加载成功
         */
        private fun onAdLoaded() {
            onAdLoadComplete {
                Logger.logI(logTag, "onAdLoaded")
                callback?.onAdLoaded?.invoke()

                //进行广告展示
                if (tpAdStatus != null && !config.isOnlyPreload && nativeAdLayout != null) {
                    showAd()
                }
                startNext(this)
            }
        }

        /**
         * 广告加载失败
         */
        private fun onAdLoadFailed(adError: AdError?) {
            onAdLoadComplete {
                Logger.logI(logTag, "onAdLoadFailed --- adError:$adError")
                callback?.onAdLoadFailed?.invoke(adError)
                startNext(this)
            }
        }

        /**
         * 广告加载完成
         */
        private fun onAdLoadComplete(callback: () -> Unit) {
            tpAdStatus?.isRequesting = false

            if (tpAdStatus?.isLoadCompleted == false) {
                tpAdStatus?.isLoadCompleted = true
                removeLoader(this)
                callback.invoke()
            }
        }

        /**
         * 广告展示失败
         */
        private fun onAdShowFailed(tag: Int) {
            Logger.logI(logTag, "onAdShowFailed --- tag:$tag")
            tpAdStatus?.isShowing = false
            callback?.onAdShowFailed?.invoke()
        }

        /**
         * 获取缓存信息
         */
        fun checkValidAdCaches(): List<ATAdInfo> {
            val atNative = this.atNative ?: getAtNative()
            return atNative.checkValidAdCaches() ?: listOf()
        }

        /**
         * 广告释放
         */
        fun onAdDestroy() {
            nativeAd?.destory()
            nativeAd = null
        }

        /**
         * 广告释放
         */
        fun onAdRelease() {
            nativeAd?.destory()
            nativeAd = null
            nativeAdLayout?.removeAllViews()
            nativeAdLayout = null
            tpAdStatus = null
            callback = null
        }

        internal fun onAdVisible(isVisible: Boolean) {
            if (!isVisible || tpAdStatus?.isVisibleRender != true) return
            tpAdStatus?.isVisibleRender = false
            startRender()
        }

        override fun onPause(owner: LifecycleOwner) {
            nativeAd?.onPause()
        }

        override fun onResume(owner: LifecycleOwner) {
            nativeAd?.onResume()
        }

        override fun onDestroy(owner: LifecycleOwner) {
            owner.lifecycle.removeObserver(this)
            if (!config.isIgnoreDestroy) {
                atNative?.setAdListener(null)
                atNative?.setAdSourceStatusListener(null)
                onAdRelease()
                removeLoader(this)
            }
        }
    }

    @Keep
    data class TpNativeAdStatus(
        val placementId: String,                //广告位id
        var isStarted: Boolean = false,         //广告是否已开始加载
        var isRequesting: Boolean = false,      //广告正在请求中
        var isShowing: Boolean = false,         //广告正在展示中
        var isLoadCompleted: Boolean = false,   //广告已加载完成（加载成功 or 加载失败）
        var isVisibleRender: Boolean = false,   //是否在视图真实可见时进行渲染广告
    )

    @Keep
    data class TpNativeAdConfig(
        var placementId: String,                //广告位id
        var adViewWidth: Int,                   //广告视图宽度，大于0生效
        var adViewHeight: Int,                  //广告视图高度，大于0生效
        var isAdaptiveHeight: Boolean = true,   //是否自适应高度，目前仅支持穿山甲、广点通
        var isOnlyPreload: Boolean = false,     //是否仅预加载，不立即展示广告
        var isAutoLoadAd: Boolean = true,       //是否自动预加载广告
        var isVisibleRender: Boolean = true,    //是否在视图真实可见时进行渲染广告
        var isIgnoreDestroy: Boolean = false,   //是否忽略页面onDestroy，可继续加载广告（一般用于体外特殊情况）
        var isSelfRender: Boolean = false,      //是否为自渲染广告
    )

    class TpNativeAdCallback {
        var onAdStart: ((adLoader: TpNativeAdLoader) -> Unit)? = null
        var onAdLoaded: (() -> Unit)? = null
        var onAdLoadFailed: ((adError: AdError?) -> Unit)? = null
        var onAdRender: (() -> Unit)? = null
        var onAdShow: ((atAdInfo: ATAdInfo?) -> Unit)? = null
        var onAdShowFailed: (() -> Unit)? = null
        var onAdClick: ((atAdInfo: ATAdInfo?) -> Unit)? = null
        var onAdClose: ((atAdInfo: ATAdInfo?) -> Boolean)? = null

        fun onAdStart(onAdStart: (adLoader: TpNativeAdLoader) -> Unit) {
            this.onAdStart = onAdStart
        }

        fun onAdLoaded(onAdLoaded: () -> Unit) {
            this.onAdLoaded = onAdLoaded
        }

        fun onAdLoadFailed(onAdLoadFailed: (adError: AdError?) -> Unit) {
            this.onAdLoadFailed = onAdLoadFailed
        }

        fun onAdRender(onAdRender: () -> Unit) {
            this.onAdRender = onAdRender
        }

        fun onAdShow(onAdShow: (atAdInfo: ATAdInfo?) -> Unit) {
            this.onAdShow = onAdShow
        }

        fun onAdShowFailed(onAdShowFailed: () -> Unit) {
            this.onAdShowFailed = onAdShowFailed
        }

        fun onAdClick(onAdClick: (atAdInfo: ATAdInfo?) -> Unit) {
            this.onAdClick = onAdClick
        }

        fun onAdClose(onAdClose: (atAdInfo: ATAdInfo?) -> Boolean) {
            this.onAdClose = onAdClose
        }
    }
}